package com.itjiange.codesandbox;

import cn.hutool.core.io.FileUtil;
import com.itjiange.model.ExecuteCodeRequest;
import com.itjiange.model.ExecuteCodeResponse;
import com.itjiange.model.ExecuteMessage;
import com.itjiange.model.JudgeInfo;
import com.itjiange.utils.ProcessUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

/**
 * @author ItJianGe
 * @data 2024/4/2 8:09
 */
@Slf4j
public class JavaCodeSandBoxTemplate implements CodeSandBox {
    public static final String GLOBAL_CODE_DIR = "FileCode";
    public static final String GLOBAL_CLASS_NAME = "Main.java";

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest codeRequest) {

        List<String> input = codeRequest.getInput();
        String code = codeRequest.getCode();
        String language = codeRequest.getLanguage();

        // 1. 获取用户代码文件
        File userCodeFile = getUserCodeFile(code);

        // 2. 编译代码得到 .class 文件
        ExecuteMessage executeMessage = getCompileFile(userCodeFile);

        // 3. 执行 .class 文件
        List<ExecuteMessage> executeMessageList = getExecuteFile(userCodeFile, input);

        // 4. 封装返回程序执行结果
        ExecuteCodeResponse executeCodeResponse = getExecuteCodeResponse(executeMessageList, executeMessage);

        // 5. 文件清理
        boolean flag = clearFile(userCodeFile);
        if (!flag) {
            log.error("文件清理失败");
        }
        return executeCodeResponse;
    }

    /**
     * 1. 获取用户代码文件
     *
     * @param userCode
     * @return
     */
    public File getUserCodeFile(String userCode) {
        String userDir = System.getProperty("user.dir");
        String globalCodeParentPath = userDir + File.separator + GLOBAL_CODE_DIR;

        // 如果该路径中不存在文件夹则创建
        if (!FileUtil.exist(globalCodeParentPath)) {
            FileUtil.mkdir(globalCodeParentPath);
        }

        // 用户代码隔离存放
        String userCodeParentPath = globalCodeParentPath + File.separator + UUID.randomUUID();
        String userCodePath = userCodeParentPath + File.separator + GLOBAL_CLASS_NAME;
        File userCodeFile = FileUtil.writeString(userCode, userCodePath, StandardCharsets.UTF_8);
        return userCodeFile;
    }

    /**
     * 2. 获取用户的编译代码
     *
     * @param userCodeFile
     * @return
     */
    public ExecuteMessage getCompileFile(File userCodeFile) {
        ExecuteMessage executeMessage = new ExecuteMessage();
        String executeCode = String.format("javac -encoding utf-8 %s", userCodeFile.getAbsolutePath());
        try {
            Process compileProcess = Runtime.getRuntime().exec(executeCode);
            executeMessage = ProcessUtil.runProcessInfo(compileProcess, "编译");
        } catch (IOException e) {
            // 异常处理
            executeMessage.setErrorMessage(e.getMessage());
            e.printStackTrace();
        }
        return executeMessage;
    }

    /**
     * 3. 执行 java 文件
     *
     * @param userCodeFile
     * @param inputList
     * @return
     */
    public List<ExecuteMessage> getExecuteFile(File userCodeFile, List<String> inputList) {
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        ExecuteMessage runProcessInfo = new ExecuteMessage();
        for (String inputCase : inputList) {
            String runCode = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s Main %s", userCodeFile.getParent(), inputCase);
            try {
                Process runProcess = Runtime.getRuntime().exec(runCode);
                // 创建守护线程，在程序执行 3 秒后自动停止
                ExecuteMessage finalRunProcessInfo = runProcessInfo;
                new Thread(() -> {
                    try {
                        Thread.sleep(5000);
                        System.out.println("达到时间限制了，中断程序");
                        runProcess.destroy();
                    } catch (InterruptedException e) {
                        finalRunProcessInfo.setMessage(e.getMessage());
                        e.printStackTrace();
                    }
                }).start();
                runProcessInfo = ProcessUtil.runProcessInfo(runProcess, "运行");
                System.out.println("执行信息：" + runProcessInfo);
            } catch (IOException e) {
                // 异常处理
                runProcessInfo.setErrorMessage(e.getMessage());
                e.printStackTrace();
            } finally {
                executeMessageList.add(runProcessInfo);
            }
        }
        return executeMessageList;
    }

    /**
     * 4. 程序执行后的返回结果
     *
     * @param executeMessageList
     * @param executeMessage
     * @return
     */
    public ExecuteCodeResponse getExecuteCodeResponse(List<ExecuteMessage> executeMessageList, ExecuteMessage executeMessage) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        List<String> outputList = new LinkedList<>();
        String errorExecMessage = executeMessage.getErrorMessage();
        if (errorExecMessage != null) {
            outputList.add(errorExecMessage);
        }
        long maxTime = 0; // 获取程序执行各个用例中的最长时间
        for (int i = 0; i < executeMessageList.size(); i++) {
            ExecuteMessage execMessage = executeMessageList.get(i);
            String errorMessage = execMessage.getErrorMessage();
            if (errorMessage != null) {
                outputList.add(errorMessage);
                executeCodeResponse.setStatue(3);
                break;
            }
            Long time = execMessage.getTime();
            if (time != null) {
                maxTime = Math.max(maxTime, time);
            }
            outputList.add(execMessage.getMessage());
        }
        executeCodeResponse.setOutput(outputList);
        if (outputList.size() == executeMessageList.size()) {
            executeCodeResponse.setStatue(0);
        }
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        judgeInfo.setMemory(executeMessage.getMemory());

        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }

    /**
     * 5. 文件清理
     *
     * @param userCodeFile
     * @return
     */
    public boolean clearFile(File userCodeFile) {
        if (userCodeFile.getParentFile() != null) {
            boolean delFlag = FileUtil.del(userCodeFile.getParent());
            System.out.println("文件清理" + (delFlag ? "成功" : "失败"));
            return delFlag;
        }
        return true;
    }


    /**
     * 6. 异常状态处理
     *
     * @param e
     * @return
     */
    public ExecuteCodeResponse getErrorExecute(Throwable e) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutput(new ArrayList<>());
        executeCodeResponse.setMessage(e.getMessage());
        executeCodeResponse.setStatue(2);
        executeCodeResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeResponse;
    }
}
